/*
    $Header: /Windows/Shell/ShellExt.root/ShellExt/src/CStream.cpp 1     08-01-05 0:08 Ken $
    $Log: /Windows/Shell/ShellExt.root/ShellExt/src/CStream.cpp $
 * 
 * 1     08-01-05 0:08 Ken
*/

#include "StdAfx.h"
#include ".\cstream.h"


HANDLE CStream::CreateStrm(const TCHAR *pszFile, const TCHAR *pszStrm, DWORD dwDesiredAccess, DWORD dwCreationDispostion)
{
    CString cstrName;
    cstrName.Format(_T("%s:%s"),pszFile, pszStrm);
    return CStream::CreateStrm(cstrName.GetBuffer(), dwDesiredAccess, dwCreationDispostion);
}
HANDLE CStream::CreateStrm(const TCHAR *pszFullName, DWORD dwDesiredAccess, DWORD dwCreationDispostion)
{
    return CreateFile(pszFullName, dwDesiredAccess, 0, NULL, dwCreationDispostion, 0, NULL);
}

BOOL CStream::DelStrm(TCHAR* pszFile, TCHAR* pszStrm)
{
    CString cstrName;
    cstrName.Format(_T("%s:%s"),pszFile, pszStrm);
    return CStream::DelStrm(cstrName.GetBuffer());
}

BOOL CStream::HasStrm(TCHAR* pszFile)
{
    CStream strm;
    DWORD dwNameLen;
    if(FALSE!=strm.BegEnum(pszFile))
    {
        strm.Next(&dwNameLen, 0);    // the main stream
        if(FALSE!=strm.Next(&dwNameLen, 0))
        {
            strm.EndEnum();
            return TRUE;
        }
        strm.EndEnum();
    }
    return FALSE;
}

CStream::~CStream(void)
{
    if(INVALID_HANDLE_VALUE!=m_hFile)
        CloseHandle(m_hFile);
}

BOOL CStream::BegEnum(TCHAR* pszFile)
{
    if(NULL!=pszFile)
    {
        m_cstrName=pszFile;
    }

    if(INVALID_HANDLE_VALUE!=m_hFile)
    {
        CloseHandle(m_hFile);
    }

    // Open the file
    m_hFile=CreateFile(m_cstrName.GetBuffer(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
    FILE_FLAG_BACKUP_SEMANTICS, NULL);
    if(INVALID_HANDLE_VALUE==m_hFile)
        return FALSE;

    m_lpContext=NULL;
    return TRUE;
};

BOOL CStream::Next(DWORD* pdwNameSize, DWORD* pdwDataSize, BOOL fSkip)
{
    if(INVALID_HANDLE_VALUE==m_hFile)
        return FALSE;

    DWORD dwRead;
    ZeroMemory(&m_sid, sizeof(m_sid));

    // get stream head
    if( (FALSE==BackupRead(m_hFile, (LPBYTE) &m_sid, WIN32_STREAM_ID_SIZE, &dwRead, FALSE, FALSE, &m_lpContext))
        || (dwRead != WIN32_STREAM_ID_SIZE) )
    {
        return FALSE;
    }

    if(NULL!=pdwDataSize){*pdwNameSize=m_sid.Size.LowPart;}
    if(NULL!=pdwNameSize){*pdwNameSize=m_sid.dwStreamNameSize;}

    if( ( (0==m_sid.Size.LowPart) && (0==m_sid.Size.HighPart) )
        && (0==m_sid.dwStreamNameSize) )
    {
        return FALSE;
    }

    //
    // At this point, we've read the header of the stream.
    // What follows is the name of the stream and next its content.
    //

    DWORD dw1, dw2;
    if(0!=m_sid.dwStreamNameSize)
    {
        if(FALSE==fSkip)
        {
            // Read the stream name
            ZeroMemory(&m_wszStrmName, sizeof(m_wszStrmName));
            BackupRead(m_hFile, (LPBYTE)m_wszStrmName, m_sid.dwStreamNameSize, &dwRead, FALSE, FALSE, &m_lpContext);
            if (dwRead != m_sid.dwStreamNameSize)
                return FALSE;

            // A stream name is stored enclosed in a pair of colons plus a $DATA default trailer.
            // If the stream name is VersionInfo it's stored (and retrieved) as:
            // :VersionInfo:$DATA
            // get the VersionInfo part
            LPWSTR pw = wcsstr(m_wszStrmName+1, L":");
            *pw=0;
            m_dwNameLen=pw-m_wszStrmName;
            if(NULL!=pdwNameSize){*pdwNameSize=m_dwNameLen;}
        }
        else
        {
            BackupSeek(m_hFile, m_sid.dwStreamNameSize, 0, &dw1, &dw2, &m_lpContext);
        }
    }

    if( (0!=m_sid.Size.LowPart) || (0!=m_sid.Size.HighPart) )
    {
        // Skip the stream body
        BackupSeek(m_hFile, m_sid.Size.LowPart, m_sid.Size.HighPart, &dw1, &dw2, &m_lpContext);
    }

    return TRUE;
}

DWORD CStream::GetName(TCHAR* pszName, DWORD dwSize)
{
    if(INVALID_HANDLE_VALUE==m_hFile)
        return 0;

    USES_CONVERSION;
    _tcsncpy(pszName, W2T(m_wszStrmName+1), min(dwSize, m_dwNameLen));
    return m_dwNameLen;
}

DWORD CStream::Skip(DWORD dwNum)
{
    if(INVALID_HANDLE_VALUE==m_hFile)
        return 0;

    DWORD i;
    for(i=0; i<dwNum; i++)
    {
        if(FALSE==Next(NULL, NULL, TRUE))
        {
            break;
        }
    }
    return i;
}

BOOL CStream::Reset()
{
    if(INVALID_HANDLE_VALUE==m_hFile)
        return FALSE;

    CloseHandle(m_hFile);
    return BegEnum(m_cstrName.GetBuffer());
}
